package com.ht.aems.fesservice;

import com.ht.aems.dbservice.ConnectionFactory;
import com.ht.aems.dbservice.SqlExecutor;
import com.ht.aems.fesmodel.FesPersistence;
import com.ht.aems.fesmodel.FesProperties;
import com.ht.aems.fesmodel.PointManager;
import com.ht.aems.fesmodel.data.AnalogPoint;
import com.ht.aems.fesmodel.data.DiscretePoint;
import com.ht.aems.fesmodel.data.MeasurePoint;
import com.ht.aems.fesmodel.data.YXData;
import org.apache.log4j.Logger;

import java.rmi.RemoteException;
import java.sql.Connection;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by IntelliJ IDEA.
 *
 * @author Dong Shufeng
 *         Date: 2008-7-23
 */
public class Sampler {
    private static Logger log = Logger.getLogger(Sampler.class);

    //public final static int INTERVAL = 5;
    private static SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
    private static SimpleDateFormat formatter2 = new SimpleDateFormat("yyyyMM");
    private static SimpleDateFormat formatter3 = new SimpleDateFormat("yyyy");
    private Map<Integer, Long> analogSampledTime = new HashMap<Integer, Long>();
    private Map<Integer, Long> discreteSampledTime = new HashMap<Integer, Long>();
    private static String hisDb;

    public Sampler() {
        hisDb = FesProperties.getProperty(FesProperties.FES_HIS_DB_NAME).trim();
    }

    public void sample() {
        log.info("Sample data to history database start...");
        if (hisDb == null) {
            log.warn("History database name is null, sample failed.");
            return;
        }
        Connection conn = ConnectionFactory.createConnection(hisDb);
        if (conn == null) {
            log.warn("Connection can not be built to db, sample failed.");
            return;
        }
        boolean isReloaded = false;
        try {
            isReloaded = RemoteFesServer.getInstance().isSamplerPointsReloaded();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        log.info("is SamplerPoints reloaded statu: " + isReloaded);
        try {
            if(!isReloaded) {
                log.info("SamplerPoints need reload,start PointManager reload...");
                System.out.println("SamplerPoints need reload,start PointManager reload...");
                PointManager.getInstance().reload();
                RemoteFesServer.getInstance().setSamplerPointsReloaded(true);
            }
        } catch (RemoteException e) {
            log.warn(e.getMessage());
            e.printStackTrace();
        }
        List<String> batch = new ArrayList<String>();
        for (AnalogPoint p : PointManager.getInstance().getAnalogPoints().values()) {
            Double v = readRtAnalog(p);
            if (v == null || p.getTimestamp() == null)
                continue;
            if (analogSampledTime.containsKey(p.getId()) && analogSampledTime.get(p.getId()) == p.getTimestamp().getTime())
                continue;
            p.setEngValue(v);
            makeSql(p, batch);
        }
        for (DiscretePoint p : PointManager.getInstance().getDiscreatePoints().values()) {
            Integer v = readRtDiscrete(p);
            if (v == null || p.getTimestamp() == null)
                continue;
            if (discreteSampledTime.containsKey(p.getId()) && discreteSampledTime.get(p.getId()) == p.getTimestamp().getTime())
                continue;
            p.setEngValue(v);
            makeSql(p, batch);
        }
        boolean b = true;
        if (batch.size() > 0) {
            b = SqlExecutor.executeBatch(conn, batch);
            if (b) {
                for (AnalogPoint p : PointManager.getInstance().getAnalogPoints().values())
                    if ((p.getTimestamp() != null && !analogSampledTime.containsKey(p.getId())) ||
                            (p.getTimestamp() != null && p.getTimestamp().getTime() != analogSampledTime.get(p.getId())))
                        analogSampledTime.put(p.getId(), p.getTimestamp().getTime());
                for (DiscretePoint p : PointManager.getInstance().getDiscreatePoints().values())
                    if ((p.getTimestamp() != null && !discreteSampledTime.containsKey(p.getId())) ||
                            (p.getTimestamp() != null && p.getTimestamp().getTime() != discreteSampledTime.get(p.getId())))
                        discreteSampledTime.put(p.getId(), p.getTimestamp().getTime());
            }
        }
        log.info("Sample realtime data to history database per 5 minutes " + (b ? "successful." : "failed."));
    }

    public boolean update(MeasurePoint p) {
        if (hisDb == null) {
            log.warn("History database name is null, update failed.");
            return false;
        }
        Connection conn = ConnectionFactory.createConnection(hisDb);
        if (conn == null) {
            log.warn("Connection can not be built to db, update failed.");
            return false;
        }
        List<String> batch = new ArrayList<String>();
        makeSql(p, batch);
        return batch.size() > 0 && SqlExecutor.executeBatch(conn, batch);
    }

    private void makeSql(MeasurePoint p, List<String> batch) {
        String sql;
        String tableName = "";
        String valueColumnName;
        Number v;
        if (p instanceof AnalogPoint) {
            v = ((AnalogPoint) p).getEngValue();
            tableName += FesPersistence.TABLE_ALG + "_";
            valueColumnName = "fValue";
        } else if (p instanceof DiscretePoint) {
            v = ((DiscretePoint) p).getEngValue();
            tableName += FesPersistence.TABLE_DIS + "_";
            valueColumnName = "nValue";
        } else
            return;
        if (p.getTimestamp() == null)
            return;
        long timeStamp = p.getTimestamp().getTime();
        Calendar c = Calendar.getInstance();
        c.setTimeInMillis(timeStamp);
        int hour = c.get(Calendar.HOUR_OF_DAY);
        int minute = c.get(Calendar.MINUTE);
        int m = hour * 60 + minute;
        int dayOfMonth = c.get(Calendar.DAY_OF_MONTH);
        int month = c.get(Calendar.MONTH);
        int year = c.get(Calendar.YEAR);
        int measureType = p.getMeasureType();
        switch (measureType) {
            case MeasurePoint.MINUTE:
                tableName += formatter.format(c.getTime());
                sql = "delete from " + tableName + " where nid=" + p.getId() + " and nMinute=" + m;
                batch.add(sql);
                sql = "insert into " + tableName + "(nid, nMinute, " + valueColumnName + ") " + "values(" + p.getId() + ", " + m + ", " + v + ")";
                batch.add(sql);
                break;
            case MeasurePoint.DAY:
                tableName += formatter2.format(c.getTime());
                sql = "delete from " + tableName + " where nid=" + p.getId() + " and nDay=" + dayOfMonth;
                batch.add(sql);
                sql = "insert into " + tableName + "(nid, nDay, " + valueColumnName + ") " + "values(" + p.getId() + ", " + dayOfMonth + ", " + v + ")";
                batch.add(sql);
                break;
            case MeasurePoint.MONTH:
                tableName += formatter3.format(c.getTime());
                sql = "delete from " + tableName + " where nid=" + p.getId() + " and nMonth=" + month;
                batch.add(sql);
                sql = "insert  into " + tableName + "(nid, nMonth, " + valueColumnName + ") " + "values(" + p.getId() + ", " + month + ", " + v + ")";
                batch.add(sql);
                break;
            case MeasurePoint.YEAR:
                if (p instanceof AnalogPoint)
                    tableName = FesPersistence.TABLE_ALG_YEAR;
                else if (p instanceof DiscretePoint)
                    tableName = FesPersistence.TABLE_DIS_YEAR;
                sql = "delete from " + tableName + " where nid=" + p.getId() + " and nYear=" + year;
                batch.add(sql);
                sql = "insert into " + tableName + "(nid, nYear, " + valueColumnName + ") " + "values(" + p.getId() + ", " + year + ", " + v + ")";
                batch.add(sql);
                break;
            default:
                break;
        }
    }

    private Double readRtAnalog(AnalogPoint p) {
        Double v = RtuDataDealer.getInstance().getValueMap().getYCValue(p.getId());
        if (v == null) v = RtuDataDealer.getInstance().getRtMeasureQuery().getYCValue(p.getId());
        if (v == null)
            System.out.println("There is no realtime yc value in value map or realtime database: " + p.getId() + "\t" + p.getName());
        Long time = RtuDataDealer.getInstance().getValueMap().getYCTime(p.getId());
        if (time == null || time == 0) time = RtuDataDealer.getInstance().getRtMeasureQuery().getYCTime(p.getId());
        if (time != null && time != 0)
            p.setTimestamp(new Date(time));
        else
            p.setTimestamp(null);
        return v;
    }

    private Integer readRtDiscrete(DiscretePoint p) {
        Integer v = RtuDataDealer.getInstance().getValueMap().getYXValue(p.getId());
        if (v == null) v = RtuDataDealer.getInstance().getRtMeasureQuery().getYXValue(p.getId());
        if (v == null)
            System.out.println("There is no realtime yx value in value map or realtime database: " + p.getId() + "\t" + p.getName());
        Long time = RtuDataDealer.getInstance().getValueMap().getYXTime(p.getId());
        if (time == null || time == 0) time = RtuDataDealer.getInstance().getRtMeasureQuery().getYXTime(p.getId());
        if (time != null && time != 0)
            p.setTimestamp(new Date(time));
        else
            p.setTimestamp(null);
        return v;
    }


    public static void sampleChangeYx(YXData[] yxs) {
        log.info("Sample change yx to history database start...");
        if (hisDb == null) {
            log.warn("History database name is null, sample change yx failed.");
            return;
        }
        Calendar c = Calendar.getInstance();
        long time;
        int hour;
        int minute;
        int m;
        List<String> sqls = new ArrayList<String>(yxs.length);
        String sql;
        String date;
        String tableName;
        for (YXData data : yxs) {
            time = data.getTimestamp();
            c.setTimeInMillis(time);
            hour = c.get(Calendar.HOUR_OF_DAY);
            minute = c.get(Calendar.MINUTE);
            m = hour * 60 + minute;
            date = formatter.format(c.getTime());
            tableName = FesPersistence.TABLE_DIS + "_" + date;
            for (int i = 0; i < data.getData().length; i++) {
                int nid = MeasurePoint.getNID(data.getRemoteUnitId(), data.getYxBit()[i]);
                sqls.add("delete from " + tableName + " where nid=" + nid + " and nMinute=" + m);
                sql = "insert into " + tableName + "(nid, nMinute, nValue) " + "values(" + nid + ", " + m + ", " + data.getData()[i] + ")";
                sqls.add(sql);
            }
        }
        boolean b = SqlExecutor.executeBatch(sqls);
        log.info("Sample realtime discrete change data to history database" + (b ? "successful." : "failed."));
    }

}
